package show.me.the.money.config.reportSchema.define;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.springframework.dao.DataAccessException;

import show.me.the.money.config.SortComparator;
import show.me.the.money.config.Sortable;
import show.me.the.money.config.reportSchema.ReportDefine;
import show.me.the.money.config.reportSchema.ReportDefineFactory;
import show.me.the.money.config.schema.SchemaItem;
import show.me.the.money.dictionary.Dictionaries;
import show.me.the.money.dictionary.Dictionary;
import show.me.the.money.util.ConfigValueParser;
import show.me.the.money.util.context.Context;

@SuppressWarnings("unchecked")
public class ReportSchemaDynamicPercent extends ReportSchema {
	private static Log logger = LogFactory.getLog(ReportSchemaDynamicPercent.class);
	
	private List dictionaryItem=null;
	
	private String unit;
	
	/**
	 * @param doc
	 * @param hash
	 *            {@list:List,@field:String}
	 */
	@SuppressWarnings("deprecation")
	public ReportSchemaDynamicPercent(Document doc, Context ctx) {
		Element root = doc.getRootElement();
		id = root.attributeValue("id", "");
		title = root.attributeValue("title", "");

		Element cnd = root.element("condition");
		if (cnd != null) {
			try {
				if(cnds == null){
					cnds = jsonMapper.readValue(cnd.getText(),List.class);
				}
				else{
					if(cnds.get(0).equals("and")){
						cnds.add(jsonMapper.readValue(cnd.getText(),List.class));
					}
					else{
						List o = new ArrayList();
						o.add("and");
						o.add(cnds);
						o.add(jsonMapper.readValue(cnd.getText(),List.class));
						cnds = o;
					}
				}
			}catch (Exception e) {
				logger.error("parse reportCondition failed:", e);
			}
		}
		List<Element> els = root.selectNodes("queryArgs/arg");
		int i = 0;
		String field=root.element("field").attributeValue("id");
		String dic = root.element("field").attributeValue("dic");
		if (dic == null) dic = field;
		boolean limit=root.element("field").attributeValue("limit")!=null?true:false;
		String defaultValue="";
		for (Element el : els) {
			SchemaItem si = new SchemaItem(el, i);
			if(si.getId().equals(field)){
				defaultValue=ConfigValueParser.parse(si.getDefaultValue(),ctx);
			}
			queryArgs.add(si);
			i++;
		}

		els = root.elements("define");
		
		int startRenderIndex = 0;
		String expr="";
		unit=(String)ctx.get("q."+field);
		if(unit==null){
			unit=defaultValue;
		}
		Dictionary dictionary = Dictionaries.instance().getDic(dic);
		dictionaryItem = dictionary.getSlice(unit, Dictionary.SLICE_ALL,
				"", 0, 0);
		for (Element el : els) {
			List<Element> headers=el.selectNodes("headers/item");
			for(Element h : headers){
				if(h.attributeValue("id").equals(field)){
					expr=h.attributeValue("expr");
					//System.out.println(unit);
					if(expr==null) continue;
					h.setAttributeValue("expr", "substr("+expr+",1,"+this.getNextLevel(unit)+")");
				}
			}
			
			try {
				if(cnds==null && expr.length()>0){
					if(limit){
						cnds=jsonMapper.readValue("['eq',['$','length("+expr+")'],['i',"+this.getNextLevel(unit)+"]]",List.class);
					}else{
						cnds=jsonMapper.readValue("['gt',['$','length("+expr+")'],['i',"+(this.getNextLevel(unit)-1)+"]]",List.class);
					}
				}
			} catch (Exception e) {
				logger.error("parse reportCondition failed:", e);
			}
			ReportDefine define = ReportDefineFactory.createInstance(el,
					startRenderIndex);
			define.setReportSchema(this);
			define.setQueryArgs(queryArgs);
			startRenderIndex += define.getHeaderCount();
			if (cnds != null) {
				define.addCondition(cnds);
			}
			defines.add(define);
		}

		Element el = root.element("digger");
		if (el != null) {
			diggers = new HashMap<String, String>();
			els = el.elements();
			for (Element dig : els) {
				diggers.put(dig.attributeValue("match"), dig
						.attributeValue("target"));
			}
		}

		el = root.element("chart");
		if (el != null) {
			chartInfo = new HashMap<String, String>();
			chartInfo.put("template", el.attributeValue("template"));
			chartInfo.put("chartType", el.attributeValue("chartType"));
			chartInfo.put("limit", el.attributeValue("limit"));
		}


	}
	/**
	 * @param code
	 * @return
	 */
	private int getNextLevel(String code) {
		switch (code.length()) {
		case 4:
			return 6;
		case 6:
			return 9;
		case 9:
			return 11;
		case 11:
			return 11;
		default:
			return code.length();
		}
	}

	public List<HashMap> run(Context ctx) throws DataAccessException {
		List<HashMap> data = null;
		if (defines.size() == 1) {
			data = defines.get(0).runSingleMode(ctx);
		} else if(defines.size()==2) {
			HashMap<String, HashMap> container = new LinkedHashMap<String, HashMap>();
			HashMap<String, HashMap> container2 = new LinkedHashMap<String, HashMap>();
			defines.get(0).runMutiMode(container, ctx);
			defines.get(1).runMutiMode(container2, ctx);
			List<String> groups=new ArrayList<String>();
			for(SchemaItem schemaItem : defines.get(0).getHeaders(true)){
				if(!groups.contains(schemaItem.getId())){
					groups.add(schemaItem.getId());
					groups.add(schemaItem.getId()+"_text");
				}
			}
//			String groupStr=defines.get(0).getHeaders(true).get(0).getId();
			Iterator<String> it=container.keySet().iterator();
			for (; it.hasNext();) {
				String key = (String) it.next();
				try {
					if (container2.containsKey(key)
							&& container2.get(key) != null) {
						HashMap<String, Double> dataItemSub=container.get(key);
						HashMap<String, Double> dataItemBase=container2.get(key);
						Iterator<String> dataKeys=dataItemSub.keySet().iterator();
						for(;dataKeys.hasNext();){
							String dataKey=dataKeys.next();
							if(!groups.contains(dataKey)){
								double sub=dataItemSub.get(dataKey);
								double base=dataItemBase.get(dataKey);
								double percent=0.0;
								if(sub!=0 && base!=0){
									percent=doubleFormat(sub/base*100);
								}
								dataItemSub.put(dataKey, percent);
							}
						}
					} else {
						HashMap<String, Double> dataItemSub=container.get(key);
						Iterator<String> dataKeys=dataItemSub.keySet().iterator();
						for(;dataKeys.hasNext();){
							String dataKey=dataKeys.next();
							if(!groups.contains(dataKey)){
								dataItemSub.put(dataKey, 0.0);
							}
						}
					}
				} catch (Exception e) {
					logger.error("ReportSchemaDynamic run failed:", e);
					container.get(key).put("KPI", 0);
				}
			}
			data = new ArrayList<HashMap>();
			data.addAll(container.values());
			ctx.put("totalCount", data.size());
			ctx.put("pageSize", data.size());
			ctx.put("pageNo", 1);
		}else{
			HashMap<String, HashMap> container = new LinkedHashMap<String, HashMap>();
			for (ReportDefine define : defines) {
				define.runMutiMode(container, ctx);
			}
			data = new ArrayList<HashMap>();
			data.addAll(container.values());
			Collections.sort(data, new Comparator() {
				public int compare(Object o1, Object o2) {
					Iterator<String> it = ((HashMap) o1).keySet().iterator();
					for (; it.hasNext();) {
						String key = (String) it.next();
						if (key.indexOf("_") > -1) {
							String str1 = (String) ((HashMap) o1).get(key
									.split("_")[0]);
							String str2 = (String) ((HashMap) o2).get(key
									.split("_")[0]);
							if (str1 == null || str2 == null)
								break;
							if (Long.parseLong(str1) >Long.parseLong(str2)) {
								return 1;
							}
						}
					}
					return 0;
				}
			});
		
			ctx.put("totalCount", data.size());
			ctx.put("pageSize", data.size());
			ctx.put("pageNo", 1);
		}
		return data;
	}
	
	public Double doubleFormat(double   d)   { 
		DecimalFormat df = new DecimalFormat( "0.## "); 
		return Double.parseDouble(df.format(d));                 
	}

}
